package com.aaron.util;


import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

import static java.util.function.Function.identity;

/**
 * 名称、姓名、邮箱、手机号、地址脱敏
 */
public class InfoMosaicUtil {

    /**
     * @author yangxin
     */
    public enum InfoType {
        // 姓名
        NAME(InfoMosaicUtil::changeName),
        // 地址
        ADDRESS(InfoMosaicUtil::changeAddress),
        // 电话
        PHONE(InfoMosaicUtil::changePhoneNumber),
        // 邮箱
        EMAIL(InfoMosaicUtil::changeemail);

        InfoType(Function<String, String> function) {
            this.function = function;
        }

        private final Function<String, String> function;

        public Function<String, String> getFunction() {
            return function;
        }

    }

    /**
     * @author yangxin
     */
    public static class Mosaic<T> {
        /**
         * get 方法
         */
        public Function<T, String> get;
        /**
         * set 方法
         */
        public BiConsumer<T, String> set;
        /**
         * 实现脱敏的枚举
         */
        public InfoType infoType;


        public Mosaic(Function<T, String> get, BiConsumer<T, String> set, InfoType infoType) {
            this.get = get;
            this.set = set;
            this.infoType = infoType;
        }

        private List<Mosaic<T>> list;

        public Mosaic() {

        }

        public Mosaic<T> builder() {
            if (CollectionUtils.isEmpty(list)) {
                list = new ArrayList<>();
            }
            return this;
        }

        public Mosaic<T> add(Function<T, String> get, BiConsumer<T, String> set, InfoType infoType) {
            list.add(new Mosaic<>(get, set, infoType));
            return this;
        }

        public List<Mosaic<T>> list() {
            Objects.requireNonNull(list);
            return list;
        }

    }


    /**
     * 名字脱敏
     *
     * @param name
     * @return
     */
    public static String changeName(String name) {
        if (StringUtils.isNotBlank(name) && name.length() > 1) {
            String newName = name.replaceAll("[^\u4E00-\u9FA5]", "");
            if (newName.length() < 2) {
                return name;
            }
            StringBuilder sb = new StringBuilder(newName);
            if (newName.length() < 4) {
                name = sb.replace(1, 2, "*").toString();
            } else {
                name = sb.replace(2, newName.length(), "**").toString();
            }
        }
        return name;
    }

    /**
     * 手机号脱敏
     *
     * @param phone
     * @return
     */
    public static String changePhoneNumber(String phone) {
        if (StringUtils.isNotBlank(phone) && phone.length() > 7) {
            StringBuilder sb = new StringBuilder(phone);
            phone = sb.replace(3, 7, "****").toString();
        }
        return phone;
    }

    /**
     * 邮箱用****号隐藏前面的字母
     *
     * @return
     */
    public static String changeemail(String email) {
        String emails = email.replaceAll("(\\w?)(\\w+)(\\w)(@\\w+\\.[a-z]+(\\.[a-z]+)?)", "$1****$3$4");
        return emails;
    }


    /**
     * {@link #changeAddress(String, String)}
     */
    public static String changeAddress(String address) {
        return changeAddress(address, "奉贤租户居委");
    }

    /**
     * 地址脱敏
     *
     * @param reserved 不需要脱敏的 “单个或多个字符”
     * @author yangxin
     */
    public static String changeAddress(String address, String reserved) {
        if (StringUtils.isBlank(address)) {
            return "";
        }
        if (StringUtils.isBlank(reserved)) {
            reserved = "奉贤区";
        }

        Supplier<Stream<String>> supplier = () -> Stream.of("省", "区", "州", "县", "市", "乡", "镇", "村", "居", "街", "路", "号", "弄", "层", "室", "楼");
        //找到所有最小单位地址即找到地址最后一段
        final int lastIndex = supplier.get()//get一个流
                .map(address::lastIndexOf)//->(int,int,int,int,int)
                .max(Comparator.comparing(identity()))//max(List<int>)
                .map(m -> m == -1 ? address.length() : m)//没找到返回长度，找到m
                .orElseThrow(RuntimeException::new);//捕捉异常

        String finalReserved = reserved;
        String b = Arrays.stream(address.substring(0, lastIndex).split(""))//分隔成char
                .map(m -> supplier.get()//将每个char去标志流里面匹配，没找到的话，如果char是预留不脱敏字符串里的字符的话就保留，否则就用*替代
                        .filter(f -> f.equals(m))//地址的chars再去匹配地址单位，只留地址单位
                        .findFirst()//找到地址单位在流中的位置：省在流中的位置，区在流中的位置...
                        .orElseGet(() -> Arrays.stream(finalReserved.split(""))
                                .filter(f -> f.equals(m))
                                .findFirst()
                                .orElse("*")))
                .reduce((t1, t2) -> t1 + t2)//将char再拼接起来
                .orElseThrow(RuntimeException::new);//抛异常

        return b + address.substring(lastIndex);
    }


    /**
     * 集合脱敏
     * <p>
     * service层脱敏参考以下代码
     * <pre>
     *        List<DASDept> list = dasDeptMapper.getListByLast(last);
     *
     *         mosaic(list, new InfoMosaicUtil.Mosaic<DASDept>().builder()
     *                 .add(DASDept::getDeptFullName, DASDept::setDeptFullName, InfoMosaicUtil.InfoType.ADDRESS)
     *                 .add(DASDept::getDeptName, DASDept::setDeptName, InfoMosaicUtil.InfoType.ADDRESS)
     *                 .add(DASDept::getDeptLinkman, DASDept::setDeptLinkman, InfoMosaicUtil.InfoType.NAME)
     *                 .add(DASDept::getDeptLinkmanEmail, DASDept::setDeptLinkmanEmail, InfoMosaicUtil.InfoType.EMAIL)
     *                 .add(DASDept::getDeptLinkmanPhone, DASDept::setDeptLinkmanPhone, InfoMosaicUtil.InfoType.PHONE)
     *                 .list());
     *         System.out.println(list);
     * </pre>
     *
     * @author yangxin
     */
    public static <T> void mosaic(Collection<T> collection, List<Mosaic<T>> list) {
        collection.forEach(c -> list.forEach(v -> {
            String value = Arrays.stream(InfoType.values()).filter(f -> f == v.infoType)//从所有的mosaic里面找选择的
                    .findFirst()//就是找选择的是哪一个InfoType而已，那直接 v.infoType 不好么？
                    .map(type -> type.getFunction().apply(Optional.ofNullable(v.get.apply(c)).orElse("")))//？？？
                    .orElse(null);//因为add(,,Infotype)的第三个参数只能是枚举，所以一定能匹配到，要不编译出错了就，所以不存在将value设置为null的时候

//            String value2 = v.get.apply(c);
            String value2 =
            v.infoType.getFunction().apply(Optional.ofNullable(v.get.apply(c)).orElse(""));

//            v.set.accept(c, value);
            v.set.accept(c, value2);
        }));
    }

    public static void main(String[] args) {
    }
}
